package cn.lxh.ymcc.service.impl;

import cn.lxh.ymcc.domain.Course;
import cn.lxh.ymcc.domain.CourseMarket;
import cn.lxh.ymcc.domain.CourseOrder;
import cn.lxh.ymcc.domain.CourseOrderItem;
import cn.lxh.ymcc.dto.CourseDto;
import cn.lxh.ymcc.dto.CourseOrderCreateParamsDto;
import cn.lxh.ymcc.dto.PayDto;
import cn.lxh.ymcc.exception.GlobalException;
import cn.lxh.ymcc.feign.CourseInfoForOrderFeignClient;
import cn.lxh.ymcc.mapper.CourseOrderMapper;
import cn.lxh.ymcc.result.JSONResult;
import cn.lxh.ymcc.service.ICourseOrderItemService;
import cn.lxh.ymcc.service.ICourseOrderService;
import cn.lxh.ymcc.util.AssertUtil;
import cn.lxh.ymcc.util.CodeGenerateUtils;
import cn.lxh.ymcc.vo.CourserOrderVo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author small.tiger
 * @since 2022-07-29
 */
@Service
@Slf4j

public class CourseOrderServiceImpl extends ServiceImpl<CourseOrderMapper, CourseOrder> implements ICourseOrderService {
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;
    @Autowired
    private CourseInfoForOrderFeignClient courseInfoForOrderFeignClient;
    @Autowired
    private ICourseOrderItemService courseOrderItemService;
    @Autowired
    private RocketMQTemplate  rocketMQTemplate;

    /**
     * 点击下单的业务luoj
     *      -校验   303 做了
     *          -检查token
     *              -key：当时的ids：loginId         loginId这里写死的  1
     *              -从redis中拿到 token 和 dto中的token 比较
     *          -业务校验 =使用feign去调 course
     *              -课程必须是上架的，课程Id不能重复
     *     -保存订单
     *          -先主要订单：金额没有
     *          -子订单；
     *          -更新主订单
     *     -删除token
     *
     * 加入RQ后业务逻辑
     *    需要rq事务
     *          -新增主表  字表的操作 需要rq事务后 和order的自增id没有了
     *
     * @param dto
     */
    @Override
    public String placeOrder(CourseOrderCreateParamsDto dto) {
        Long loginId = 1L;
        //需要处理long类型的coureId 转换成string类型的
        String courseIdsStr = StringUtils.join(dto.getCourseIds(), ",");
        //点击购买时候生成的key
        String key = courseIdsStr+":"+loginId;
        Object redisToken = redisTemplate.opsForValue().get(key);
        AssertUtil.isNotNull(redisToken,"token失效，请重新下单！");
        AssertUtil.isEquals(dto.getToken(),redisToken.toString(),"token错误，请重新下单");
        //业务校验 =使用feign去调 course调用feign接口去course中查询
        JSONResult jsonResult  = courseInfoForOrderFeignClient.searchCourseOrder(courseIdsStr);
        AssertUtil.isTrue(jsonResult.isSuccess(),"信息错误，下单失败！");
        AssertUtil.isNotNull(jsonResult.getData(),"服务不可达，下单失败！");
        /*
            存在一个强转的操作
                data是 feign接口中的所有数据
         */
        String objStr = JSON.toJSONString(jsonResult.getData());
        CourserOrderVo orderVo = JSONObject.parseObject(objStr,CourserOrderVo.class);
        List<CourseDto> courseInfos = orderVo.getCourseInfos();
        AssertUtil.isNotNull(courseInfos,"课程信息有误，下单失败");
        /*
             保存订单 （主订单-子订单）
         */
        Date date = new Date();
        CourseOrder order = new CourseOrder();
        order.setCreateTime(date);
        order.setOrderNo(CodeGenerateUtils.generateOrderSn(loginId));
        //这三个写死====
        order.setStatusOrder(0);
        order.setUserId(loginId);
        order.setVersion(0);
        //这三个写死
        order.setPayType(dto.getPayType());
        /*
           新增主表
                -加上rq事务后
                    -事务之后才能新增
         */
      //  insert(order);
        /*
            保存子订单
         */
        StringBuilder sb = new StringBuilder();
        //courseInfos 里面包含courseMarket，course
        for (CourseDto courseInfo : courseInfos) {
            Course course = courseInfo.getCourse();
            CourseMarket courseMarket = courseInfo.getCourseMarket();
            //保存子表中的数据
            CourseOrderItem item = new CourseOrderItem();
            /*
                order没有新增
                 子表的 就不需要 新增了
             */
            //item.setOrderId(order.getId());
            item.setAmount(courseMarket.getPrice());
            item.setCount(1);
            order.setTotalCount(order.getTotalCount() + 1);
            order.setTotalAmount(order.getTotalAmount().add(item.getAmount()));
            item.setCreateTime(date);
            item.setCourseId(course.getId());
            item.setCourseName(course.getName());
            item.setCoursePic(course.getPic());
            item.setVersion(order.getVersion());
            item.setOrderNo(order.getOrderNo());
            //多个名字
            sb.append(course.getName()).append(",");

            /*
                 事务消息之后
                    -
             */
            //courseOrderItemService.insert(item);
        }
        sb.append("】订单");
        /*
            更新主表
                --rq事务
                    -注释掉
         */
        order.setTitle(sb.toString());
       //updateById(order);
        //发布事务消息=============================== 事务 需要监听机制
        /*
            payDto写在pojo-pay中
                -方便其他的微服务来时使用
         */
        PayDto payDto = new PayDto(
                order.getTotalAmount(),
                order.getPayType(),
                null,
                order.getOrderNo(),
                order.getUserId(),
                "额外参数",
                order.getTitle()
        );
        log.info("发送事务消息");
        //构建消息
        /*
            一堆处理
                message的转换====消息体--公用的数据。。。需要掉用 pay 微服务公用的数据。
                    -  Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(payDto)).build()
                    -
            order的处理--将order对象传到listener监听器
                -将字表封装
                    -list<字表>
            要和监听器上注解申明的名字一致，不是yml中mq的类上的一直
         */
        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(payDto)).build();

        TransactionSendResult transactionSendResult = rocketMQTemplate.sendMessageInTransaction( //发消息有错@TODO
                "service-order-producer",//
                "course-order-topic:course-order-tag",
                message,  //消息体
                order   //额外参数
        );
        log.info("发送完事务消息--检查事务消息状态");

        LocalTransactionState localTransactionState = transactionSendResult.getLocalTransactionState();

        SendStatus sendStatus = transactionSendResult.getSendStatus();
        if(localTransactionState != LocalTransactionState.COMMIT_MESSAGE || sendStatus != SendStatus.SEND_OK ){
            log.info("校验发送事务消息状态异常");
            throw new GlobalException("下单失败！！");
        }

        //发布事务消息结束=================================



        //



        //删除防重复token
        redisTemplate.delete(key);
        return order.getOrderNo();

    }

    /**
     * 保存订单的相关操作
     *  -在mq的监听机制里面 进行操作
     *  -arg
     *      -将order对象传过来了
     *          -子订单封装成list<> 一起放在order主订单中
     * 健壮性判断
     * @param courseOrder
     */
    @Override
    public void saveOrderAndItems(CourseOrder courseOrder) {
        EntityWrapper<CourseOrder> wrapper = new EntityWrapper<>();
        wrapper.eq("order_no",courseOrder.getOrderNo());
        CourseOrder order = selectOne(wrapper);
        AssertUtil.isNull(order,"这个订单已经存在了");
        insert(courseOrder);//保存主订单
        /*
            保存子订单
         */
        List<CourseOrderItem> items = courseOrder.getItems();
        items.forEach(item->{
            item.setOrderId(courseOrder.getId());
            courseOrderItemService.insert(item);
        });
    }

    /**
     * 支付通用的
     *   -orderno===支付中的orderno
     *      -payDto.getOrderNo()
     * @param orderNo
     * @return
     */
    @Override
    public CourseOrder selectOrderByOrderNo(String orderNo) {
        EntityWrapper<CourseOrder> wrapper = new EntityWrapper<>();
        wrapper.eq("order_no",orderNo);
        CourseOrder order = selectOne(wrapper);
        log.info("执行了mq回查");
        return order;
    }

    /**
     * pay的producer到
     * order的consumer
     *
     * @param courseOrder
     */
    @Override
    public void payOrder(CourseOrder courseOrder) {
        courseOrder.setStatusOrder(CourseOrder.STATE_PAY_SUCCESS);
        courseOrder.setUpdateTime(new Date());
        updateById(courseOrder);
    }


}
